home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / hamradio / tnos-2.000 / tnos-2 / xmodem.c < prev    next >
C/C++ Source or Header  |  1996-07-18  |  17KB  |  541 lines

  1. /*
  2.  * A version of Ward Christensen's file transfer protocol for
  3.  * Unix System V or 4.2 bsd.
  4.  *
  5.  *        Emmet P. Gray, ..!ihnp4!uiucuxc!fthood!egray, 16 Aug 85
  6.  *
  7.  * Modified by Sanford Zelkovitz   08/18/86
  8.  * Last modification date = 05/20/87
  9.  * Modified for KA9Q NOS BBS - WA3DSP 2/93
  10.  */
  11.  
  12.  
  13. #include <stdio.h>
  14. #ifdef MSDOS
  15. #include <io.h>
  16. #endif
  17. #include <errno.h>
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <stdarg.h>
  21. #include "dirutil.h"
  22. #include "timer.h" 
  23. #include "socket.h"
  24. #include "mailbox.h"
  25.  
  26. #if !defined(_lint) && !defined(MSDOS)
  27. static char rcsid[] OPTIONAL = "$Id: xmodem.c,v 1.9 1996/07/19 00:52:41 root Exp root $";
  28. #endif
  29.  
  30. #ifdef TIPMAIL
  31. #ifdef XMODEM
  32.  
  33. #ifdef min
  34. #undef min
  35. #endif
  36.  
  37. #ifdef __cplusplus
  38. extern "C" {
  39. #endif
  40. extern int access __ARGS((const char *, int));
  41. extern int unlink __ARGS((const char *));
  42. #ifdef __cplusplus
  43. }
  44. #endif
  45.  
  46. #define SPEED 2400       /* Serial line Baudrate */
  47.  
  48. #define MAXERRORS 10     /* max number of times to retry */
  49. #define SECSIZE   128    /* CP/M sector, transmission block */
  50. #define CPMEOF    26     /* End Of File (for CP/M) */
  51. #define SOH       1      /* Start Of Header */
  52. #define STX       2      /* Start of 1K block */
  53. #define EOT       4      /* End Of Transmission */
  54. #define ACK       6      /* ACKnowledge */
  55. #define NAK       21     /* Negative AcKnowledge */
  56. #define CAN       24     /* CANcel */
  57. #ifndef BS
  58. #define BS        8      /* Backspace */ 
  59. #endif
  60.  
  61. static int xm_recvfile __ARGS((char*,struct mbx *m));
  62. static int xm_sendfile __ARGS((char*,struct mbx *m));
  63. static char getchar_t __ARGS((int thesocket));
  64. static void update_crc __ARGS((unsigned char c, unsigned char *crc1, unsigned char *crc2));
  65. static void error __ARGS((struct mbx *m));
  66. static void print_text __ARGS((struct mbx *m,const char *fmt, ...));
  67. static void rawmode __ARGS((struct mbx *));
  68. static void restoremode __ARGS((struct mbx *));
  69.  
  70. int
  71. doxmodem(mode, filename, p)
  72. char mode, *filename;
  73. void *p;
  74. {
  75.         int exit_return=0, oldflush;
  76.         struct mbx *m;
  77.         
  78.         m = (struct mbx *)p;
  79.         oldflush=setflush(m->user,-1);
  80.     
  81.         switch (mode) {
  82.                 case 'r':
  83.                 case 'R':        
  84.                         exit_return=xm_recvfile(filename,m);
  85.                         break;
  86.                 case 's':
  87.                 case 'S':
  88.                         exit_return=xm_sendfile(filename,m);
  89.                         break;
  90.                 default :
  91.                         print_text(m,"Xmodem: Invalid Option\n");
  92.         }
  93.         restoremode(m);
  94.         usputc(m->user,'\n');
  95.         usflush(m->user);
  96.         setflush(m->user,oldflush);
  97.         return(exit_return);
  98. }
  99.  
  100. /* send a file to the remote */
  101. static int
  102. xm_sendfile(tfile, m)
  103. char *tfile;
  104. struct mbx *m;
  105. {
  106.         FILE *fp;
  107.         unsigned char chr, checksum, block, sector[SECSIZE];
  108.         unsigned char crc1, crc2, mode, errcount, errcount2, two_can;
  109.         int i, nbytes, speed=SPEED;
  110.         long size, min, sec;
  111.         
  112.         if ((size=fsize(tfile))==-1){
  113.                 print_text(m,"xmodem: Can't open '%s' for read\n", tfile);
  114.                 return(1);
  115.         }
  116.  
  117.         if (!(fp = fopen(tfile, READ_BINARY))) {
  118.                 print_text(m,"xmodem: Can't open '%s' for read\n", tfile);
  119.                 return(1);
  120.         }
  121.         
  122.         size=(size/128L)+1L;
  123.         sec = size * 128L * 15L / speed;
  124.         min = sec / 60L;
  125.         sec = sec - min * 60L;
  126.         print_text(m,"\nFile open: %d records\n", size);
  127.         print_text(m,"Send time: %ld min, %ld sec at %d baud\n", min, sec, speed);
  128.         print_text(m,"To cancel: use CTRL-X numerous times\n");
  129.         print_text(m,"Waiting receive ready signal\n");
  130.  
  131.         mspause(3000L);
  132.         rawmode(m); 
  133.         errcount = 0;
  134.         mode = 0;
  135.         two_can=0;
  136.         block = 1;
  137.  
  138.         while (errcount < MAXERRORS) {
  139.                 chr = getchar_t(m->user);
  140.                 if (chr == NAK)                        /* checksum mode */
  141.                         break;
  142.                 if (chr == 'C') {                /* CRC mode */
  143.                         mode = 1;
  144.                         break;
  145.                 }
  146.                 if (chr == CAN) {
  147.                         if (two_can)  {
  148.                              mspause(3000L);
  149.                              print_text(m,"\nxmodem: Abort request received\n");
  150.                              fclose(fp);
  151.                              return(1);
  152.                          }
  153.                         two_can=1;
  154.                 } else {       
  155.                         two_can=0;
  156.                 }
  157.  
  158.                 errcount++;
  159.         }
  160.         if (errcount >= MAXERRORS) {
  161.                 mspause(3000L);
  162.                 print_text(m,"xmodem: Timed out on acknowledge\n");
  163.                 fclose(fp);
  164.                 return(1);
  165.         }
  166.         two_can=0;
  167.         while ((nbytes= fread(sector,1,128, fp))!=0) {
  168.                 if (nbytes < SECSIZE) {              /* fill short sector */
  169.                         for (i=nbytes; i < SECSIZE; i++)
  170.                                 sector[i] = CPMEOF;
  171.                 }
  172.                 errcount = 0;
  173.                 while (errcount < MAXERRORS) {
  174.                         usputc(m->user,SOH);        /* the header */
  175.                         usputc(m->user,block);      /* the block number */
  176.                         chr = ~block;
  177.                         usputc(m->user,chr);        /* it's complement */
  178.                         checksum = 0;
  179.                         crc1 = 0;
  180.                         crc2 = 0;
  181.                         for (i=0; i < SECSIZE; i++) {
  182.                                 usputc(m->user,sector[i]);
  183.                                 if (mode)
  184.                                         update_crc(sector[i],&crc1,&crc2);
  185.                                 else
  186.                                         checksum += sector[i];
  187.                         }
  188.                         if (mode) {
  189.                                 update_crc(0,&crc1,&crc2);
  190.                                 update_crc(0,&crc1,&crc2);
  191.                                 usputc(m->user,crc1);
  192.                                 usputc(m->user,crc2);
  193.                         
  194.                         }
  195.                         else
  196.                                 usputc(m->user,checksum);  
  197.                         
  198.                         usflush(m->user);
  199.                         errcount2=0;
  200. rec_loop:
  201.                         
  202.                         chr = getchar_t(m->user);
  203.                         if (chr == CAN) {
  204.                                 if (two_can)  {
  205.                                         mspause(3000L);
  206.                                         print_text(m,"\nxmodem: Abort request received\n");
  207.                                         fclose(fp);
  208.                                         return(1);
  209.                                 }
  210.                                 two_can=1;
  211.                         } else {       
  212.                                 two_can=0;
  213.                         }
  214.                         
  215.                         if (chr == ACK)
  216.                                 break;                /* got it! */
  217.                          /* noise on line? */
  218.                         if (chr != NAK ) {
  219.                                 ++errcount2;
  220.                                 if (errcount2>=MAXERRORS) {
  221.                                           error(m);
  222.                                           fclose(fp);
  223.                                           return 1;
  224.                                 }
  225.                                 goto rec_loop;   
  226.                         }        
  227.                         errcount++;
  228.                 }
  229.                 if (errcount >= MAXERRORS) {
  230.                         error(m);
  231.                         fclose(fp);
  232.                         return(1);
  233.                 }
  234.                 block++;
  235.         }
  236.         errcount = 0;
  237.         while (errcount < MAXERRORS) {
  238.                 usputc(m->user,EOT);
  239.                 usflush(m->user);
  240.                 if (getchar_t(m->user) == ACK)
  241.                         {
  242.                         fclose(fp);
  243.                         mspause(6000L);
  244.                         log(m->user,"Xmodem: Download - %s",tfile);
  245.                         print_text(m,"Xmodem: File sent OK\n");
  246.                         return(0);
  247.                         }
  248.                 errcount++;
  249.         }
  250.         fclose(fp);
  251.         mspause(3000L);
  252.         error(m);
  253.         return(1);
  254. }
  255.  
  256. /* receive a file from the remote */
  257. static int
  258. xm_recvfile(tfile, m)
  259. char *tfile;
  260. struct mbx *m;
  261. {
  262.         FILE *fp;
  263.         unsigned char hdr, blk, cblk, tmp, thecksum, crc1, crc2;
  264.         unsigned char c1, c2, sum, block, sector[SECSIZE];
  265.         unsigned char first, mode, errcount, two_can;
  266.         int i;
  267.        
  268.         if (!access(tfile, 00)) {
  269.                 print_text(m,"xmodem: File %s already exists\n",tfile);
  270.                 return(1);
  271.         }
  272.  
  273.         if (!(fp = fopen(tfile, WRITE_BINARY))) {
  274.              print_text(m,"xmodem: Can't open '%s' for write\n", tfile);
  275.              return(1);
  276.         }
  277. #ifdef __GNUC__
  278.     c1 = 0;            /* silence warnings */
  279.     c2 = 0;
  280.     thecksum = 0;
  281. #endif
  282.         print_text(m,"File open - ready to receive\n");
  283.         print_text(m,"To cancel: use CTRL-X numerous times\n");
  284.         mspause(3000L);
  285.         rawmode(m);  
  286.         errcount = 0;
  287.         block = 1;
  288.         first=0;
  289.         two_can=0;
  290.         
  291.         mspause(3000L);
  292.         while (errcount < MAXERRORS) {
  293.                 if (errcount < (MAXERRORS / 2)) {
  294.                       usputc(m->user,'C');                /* try CRC mode first */
  295.                       usflush(m->user);
  296.                       mode = 1;
  297.                 }
  298.                 else {
  299.                       usputc(m->user,NAK);                /* then checksum */
  300.                       usflush(m->user);
  301.                       mode = 0;
  302.                 }
  303.                 if ((hdr = getchar_t(m->user)) == SOH) {
  304.                         first=1;
  305.                         break;
  306.                 }
  307.                 if (hdr == CAN) {
  308.                         if (two_can){
  309.                                 mspause(3000L);
  310.                                 print_text(m,"\nxmodem: Abort request received\n");
  311.                                 fclose(fp);
  312.                                 unlink(tfile);
  313.                                 return(1);
  314.                         }      
  315.                         two_can=1;
  316.                 } else {       
  317.                         two_can=0;
  318.                 }
  319.                 errcount++;
  320.         }
  321.         if (errcount >= MAXERRORS) {
  322.                 mspause(3000L);
  323.                 print_text(m,"\nxmodem: Timed out on acknowledge\n");
  324.                 fclose(fp);
  325.                 unlink(tfile);
  326.                 return(1);
  327.         }
  328.         errcount = 0;
  329.         two_can=0;
  330.         while (errcount < MAXERRORS) {
  331.                 
  332.                 if (first) {
  333.                         hdr=SOH;
  334.                         first=0;
  335.                 } else
  336.                         hdr = getchar_t(m->user);
  337.                 
  338.                 if (hdr == CAN) {
  339.                         if (two_can){
  340.                                 mspause(3000L);
  341.                                 print_text(m,"\nxmodem: Abort request received\n");
  342.                                 fclose(fp);
  343.                                 unlink(tfile);
  344.                                 return(1);
  345.                         }      
  346.                         two_can=1;
  347.                         continue;
  348.                 } else {       
  349.                         two_can=0;
  350.                 }
  351.                 
  352.                 if (hdr == EOT)                        /* done! */
  353.                         break;
  354.                 
  355.                 if (hdr != SOH) {             /* read in junk for 6 seconds */
  356.                         alarm(6000L);
  357.                         while(errno != EALARM)
  358.                               rrecvchar(m->user);          
  359.                         alarm(0L);   /* cancel alarm */
  360.                         first=0;
  361.                         goto nak;
  362.                 }
  363.                 blk = getchar_t(m->user);
  364.                 cblk = getchar_t(m->user);
  365.                 crc1 = 0;
  366.                 crc2 = 0;
  367.                 sum = 0;
  368.                 for (i=0; i < SECSIZE; i++) {
  369.                         sector[i] = getchar_t(m->user);
  370.                         if (mode)
  371.                                 update_crc(sector[i],&crc1,&crc2);
  372.                         else
  373.                                 sum += sector[i];
  374.                 }
  375.                 if (mode) {
  376.                         c1 = getchar_t(m->user);
  377.                         c2 = getchar_t(m->user);
  378.                 }
  379.                 else
  380.                         thecksum = getchar_t(m->user);
  381.                 if (blk != block && blk != (block - 1))
  382.                         goto nak;
  383.                 tmp = ~blk;
  384.                 if (cblk != tmp)
  385.                         goto nak;
  386.                 if (mode) {
  387.                         update_crc(0,&crc1,&crc2);
  388.                         update_crc(0,&crc1,&crc2);
  389.                         if (c1 != crc1 || c2 != crc2)
  390.                                 goto nak;
  391.                 }
  392.                 else {
  393.                         if (thecksum != sum)
  394.                                 goto nak;
  395.                 }
  396.                 if (block == blk) {
  397.                   fflush(fp);
  398.                   if (fwrite(sector, sizeof(sector[0]), SECSIZE, fp)!=SECSIZE){
  399.                         error(m);
  400.                         print_text(m,"         File write error - Partial file deleted\n");
  401.                         fclose(fp);
  402.                         unlink(tfile);
  403.                         return (1);
  404.                   }
  405.                 }
  406.                 block = blk + 1;
  407.                 usputc(m->user,ACK);                        /* got it! */
  408.                 usflush(m->user);
  409.                 errcount = 0;
  410.                 continue;
  411.  
  412.     nak:        usputc(m->user,NAK);                    /* do it over */
  413.                 usflush(m->user);
  414.                 errcount++;
  415.         }
  416.         if (errcount == MAXERRORS) {
  417.                 error(m);
  418.                 fclose(fp);
  419.                 unlink(tfile);
  420.                 return(1);
  421.         }
  422.         usputc(m->user,ACK);
  423.         usflush(m->user);
  424.         mspause(3000L);
  425.         fclose(fp);
  426.         log(m->user,"Xmodem: Upload - %s",tfile);
  427.         print_text(m,"Xmodem: File received OK\n");
  428.         return(0);
  429. }        
  430.  
  431. /* exceeded the maximum number of retry's */
  432. static void
  433. error(m)
  434. struct mbx *m;
  435. {
  436.         int i;
  437.         
  438.         for(i=0;i<9;i++) {
  439.                 usputc(m->user,CAN);
  440.         }
  441.         usflush(m->user);
  442.         mspause(1000L);
  443.         for(i=0;i<9;i++) {
  444.                 usputc(m->user,BS);
  445.         }
  446.         usflush(m->user);
  447.         mspause(3000L);
  448.         print_text(m,"\nxmodem: Exceeded error limit...aborting\n");
  449.         return;
  450. }
  451.  
  452. /* update the CRC bytes */
  453. static void
  454. update_crc(c, crc1, crc2)
  455. unsigned char c, *crc1, *crc2;
  456. {
  457.         register int i, temp;
  458.         register unsigned char carry, c_crc1, c_crc2;
  459.         
  460.         for (i=0; i < 8; i++) {
  461.                 temp = c * 2;
  462.                 c = temp;                        /* rotate left */
  463.                 carry = ((temp > 255) ? 1 : 0);
  464.                 temp = *crc2 * 2;
  465.                 *crc2 = temp;
  466.                 *crc2 |= carry;                        /* rotate with carry */
  467.                 c_crc2 = ((temp > 255) ? 1 : 0);
  468.                 temp = *crc1 * 2;
  469.                 *crc1 = temp;
  470.                 *crc1 |= c_crc2;
  471.                 c_crc1 = ((temp > 255) ? 1 : 0);
  472.                 if (c_crc1) {
  473.                         *crc2 ^= 0x21;
  474.                         *crc1 ^= 0x10;
  475.                 }
  476.         }
  477.         return;
  478. }
  479.  
  480. /* getchar with a 5 sec time out */
  481. static char
  482. getchar_t(s)
  483. int s;
  484. {
  485.         char c;
  486.         /* only have 5 sec... */ 
  487.         alarm(5000L);
  488.         /* Wait for something to happen */
  489.         c=rrecvchar(s); 
  490.         alarm(0L);
  491.         return(c);
  492. }
  493.                           
  494. /* put the stdin/stdout in the "raw" mode */
  495. static void 
  496. rawmode(m)
  497. struct mbx *m;
  498. {
  499.         seteol(m->user,0);
  500.         seteol(m->tip->s,0);
  501.         sockmode(m->tip->s,SOCK_BINARY);
  502.         sockmode(m->user,SOCK_BINARY);
  503.         m->tip->raw=1;
  504. }
  505.  
  506. static void 
  507. restoremode(m)
  508. struct mbx *m;
  509. {
  510.         while(socklen(m->user,0) != 0)
  511.            recv_mbuf(m->user,NULL,0,NULLCHAR,0); 
  512.         seteol(m->user,"\n");
  513.         seteol(m->tip->s,"\n");
  514.         sockmode(m->user,SOCK_ASCII);
  515.         sockmode(m->tip->s,SOCK_ASCII);
  516.         m->tip->raw=0;
  517. }
  518.  
  519. void
  520. #ifdef TNOS_68K
  521. print_text(m, fmt)
  522. struct mbx *m;
  523. const char *fmt;
  524. #else
  525. print_text (struct mbx *m,const char *fmt, ...)
  526. #endif
  527. {
  528.         va_list ap;
  529.         char buf[80];
  530.  
  531.         restoremode(m);       
  532.         va_start(ap,fmt);
  533.         vsprintf(buf,fmt,ap);
  534.         va_end(ap);
  535.         usprintf(m->user,buf);
  536.         usflush(m->user);
  537. }        
  538.  
  539. #endif
  540. #endif
  541.